package com.intco.ait.quotation.api.infrastructure.service;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.intco.ait.quotation.api.common.model.AttributeVo;
import com.intco.ait.quotation.api.common.model.BomInfoDTO;
import com.intco.ait.quotation.api.common.model.BomVo;
import com.intco.ait.quotation.api.common.model.request.BomSearch;
import com.intco.ait.quotation.api.common.utils.UIDGenerator;
import com.intco.ait.quotation.api.infrastructure.mybatis.entity.AttributeDetailDo;
import com.intco.ait.quotation.api.infrastructure.mybatis.entity.BomDetailDo;
import com.intco.ait.quotation.api.infrastructure.mybatis.entity.BomTemplateDo;
import com.intco.ait.quotation.api.infrastructure.mybatis.entity.FeeDetailDo;
import com.intco.ait.quotation.api.infrastructure.mybatis.service.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class BomService {

    @Autowired
    private BomTemplateService bomTemplateService;

    @Autowired
    private FeeDetailService feeDetailService;

    @Autowired
    private FeeModelService feeModelService;

    @Autowired
    private AttributeDetailService attributeDetailService;

    @Autowired
    private BomDetailService bomDetailService;

    @Autowired
    private UIDGenerator uidGenerator;

    public Page<BomVo> search(BomSearch bomSearch) {
        QueryChainWrapper<BomTemplateDo> query = bomTemplateService.query();
        if (StringUtils.isNotBlank(bomSearch.getProductType())) {
            query.and(q -> q.eq("product_type", bomSearch.getProductType())
                    .or()
                    .eq("product_type", "通用"));
        }
        if (StringUtils.isNotBlank(bomSearch.getClassName())) {
            query.eq("class_name", bomSearch.getClassName());
        }
        if (StringUtils.isNotBlank(bomSearch.getInvCode())) {
            query.eq("inv_code", bomSearch.getInvCode());
        } else if (StringUtils.isNotBlank(bomSearch.getInvName())) {
            query.like("name", bomSearch.getInvName());
        }
        Page<BomTemplateDo> bomPage = query.orderByDesc("insert_time")
                .page(new Page<>(bomSearch.getPage(), bomSearch.getPageSize()));
        List<BomVo> bomVos = bomPage.getRecords().stream().map(this::conventToVo).collect(Collectors.toList());
        return new Page<BomVo>(bomPage.getCurrent(), bomPage.getSize(), bomPage.getTotal())
                .setRecords(bomVos);
    }

    private List<AttributeVo> getAttributes(Long bomId) {
        List<AttributeDetailDo> attributeDetailDos = attributeDetailService.query()
                .eq("bom_template_id", bomId)
                .eq("enabled", 1)
                .orderByAsc("sort")
                .list();
        return attributeDetailDos.stream().map(attributeDetailDo -> {
                    AttributeVo attributeVo = new AttributeVo();
                    attributeVo.setPropertyName(attributeDetailDo.getKey());
                    attributeVo.setPropertyValue(attributeDetailDo.getValue());
                    attributeVo.setSort(attributeDetailDo.getSort());
                    return attributeVo;
                }
        ).collect(Collectors.toList());
    }

    public List<BomInfoDTO> getBomSForSku(List<Long> bomDetailIds) {
        if (CollectionUtils.isEmpty(bomDetailIds)) {
            return Collections.emptyList();
        }
        Map<Long, BomDetailDo> bomDetailDoMap = bomDetailService.query()
                .in("id", bomDetailIds)
                .eq("is_deleted", 0).list()
                .stream().collect(Collectors.toMap(BomDetailDo::getBomTemplateId, Function.identity(),
                        BinaryOperator.maxBy(
                                Comparator.comparing(BomDetailDo::getUpdateTime))));
        List<BomTemplateDo> list = bomTemplateService.query()
                .in("id", bomDetailDoMap.keySet())
                .orderByDesc("insert_time").list();
        return list.stream().map(x -> {
            BomInfoDTO bomInfo = new BomInfoDTO();
            bomInfo.setBom(conventToVo(x));
            bomInfo.setQuantity(bomDetailDoMap.get(x.getId()).getCount());
            return bomInfo;
        }).collect(Collectors.toList());
    }

    private BomVo conventToVo(BomTemplateDo bomTemplate) {
        BomVo bomVo = new BomVo();
        bomVo.setId(bomTemplate.getId());
        bomVo.setClassId(bomTemplate.getClassId());
        bomVo.setClassName(bomTemplate.getClassName());
        bomVo.setProductType(bomTemplate.getProductType());
        bomVo.setInvCode(bomTemplate.getInvCode());
        bomVo.setName(bomTemplate.getName());
        bomVo.setBomType(bomTemplate.getBomType());
        bomVo.setAttributes(getAttributes(bomTemplate.getId()));
        FeeDetailDo feeDetailDo = feeDetailService.query().eq("bom_template_id", bomTemplate.getId()).one();
        bomVo.setFeeModel(feeModelService.query().eq("id", feeDetailDo.getFeeModelId()).one().getFeeModelName());
        bomVo.setFeeAmount(feeDetailDo.getPrice());
        bomVo.setCreateTime(bomTemplate.getInsertTime());
        bomVo.setUpdateTime(bomTemplate.getUpdateTime());
        return bomVo;
    }

    public Long creatBom(BomVo bomVo) {
        BomTemplateDo bomTemplateDo = new BomTemplateDo();
        bomTemplateDo.setId(uidGenerator.nextId());
        bomTemplateDo.setClassId(bomVo.getClassId());
        bomTemplateDo.setClassName(bomVo.getClassName());
        bomTemplateDo.setProductType(bomVo.getProductType());
        bomTemplateDo.setInvCode(bomVo.getInvCode());
        bomTemplateDo.setName(bomVo.getName());
        bomTemplateDo.setBomType(bomVo.getBomType());
        List<AttributeVo> attributes = bomVo.getAttributes();
        List<AttributeDetailDo> attributeDetailDos = new ArrayList<>(attributes.size());
        List<Long> attributeDetailId = new ArrayList<>();
        attributes.forEach(vo -> {
            AttributeDetailDo attributeDetailDo = new AttributeDetailDo();
            long id = uidGenerator.nextId();
            attributeDetailId.add(id);
            attributeDetailDo.setId(id);
            attributeDetailDo.setBomTemplateId(bomTemplateDo.getId());
            attributeDetailDo.setKey(vo.getPropertyName());
            attributeDetailDo.setValue(vo.getPropertyValue());
            attributeDetailDo.setSort(vo.getSort());
            attributeDetailDos.add(attributeDetailDo);
        });
        attributeDetailService.saveBatch(attributeDetailDos);
        bomTemplateDo.setAttributeDetailId(JSON.toJSONString(attributeDetailId));
        FeeDetailDo feeDetailDo = new FeeDetailDo();
        long feeDetailId = uidGenerator.nextId();
        feeDetailDo.setId(feeDetailId);
        feeDetailDo.setBomTemplateId(bomTemplateDo.getId());
        feeDetailDo.setPrice(bomVo.getFeeAmount());
        feeDetailDo.setFeeModelId(feeModelService.query()
                .eq("fee_model_name", bomVo.getFeeModel()).one().getId());
        feeDetailService.save(feeDetailDo);
        bomTemplateDo.setFeeDetailId(feeDetailId);
        bomTemplateService.save(bomTemplateDo);
        return bomTemplateDo.getId();
    }

    public BomTemplateDo getBomTemplateByName(String name) {
        return bomTemplateService.query().eq("name", name).one();
    }

    public void updateBom(BomVo bomVo) {
        FeeDetailDo feeDetailDo = feeDetailService.query().eq("bom_template_id", bomVo.getId()).one();
        if (feeDetailDo == null) {
            throw new IllegalArgumentException("BOM does not exist or don't have fee details");
        }
        feeDetailDo.setPrice(bomVo.getFeeAmount());
        feeDetailDo.setFeeModelId(feeModelService.query()
                .eq("fee_model_name", bomVo.getFeeModel()).one().getId());
        feeDetailDo.setUpdateTime(new Date());
        feeDetailService.updateById(feeDetailDo);
    }

    public List<String> getClassesByProductType(String productType) {
        List<String> list = bomTemplateService.query().select("distinct class_name").eq("product_type", productType).list()
                .stream().map(BomTemplateDo::getClassName).collect(Collectors.toList());
        //list.add(0, "通用");
        return list;
    }


    public long initBomDetailId(BomInfoDTO bomInfo, Long belongId, Long bomId) {
        BomDetailDo bomDetailDo = new BomDetailDo();
        long bomDetailId = uidGenerator.nextId();
        bomDetailDo.setId(bomDetailId);
        bomDetailDo.setBelongId(belongId);
        bomDetailDo.setBomTemplateId(bomId);
        bomDetailDo.setCount(bomInfo.getQuantity());
        if ("fixed".equals(bomInfo.getBom().getFeeModel())) {
            bomDetailDo.setTotalPrice(bomInfo.getBom().getFeeAmount());
        } else {
            bomDetailDo.setTotalPrice(bomInfo.getBom().getFeeAmount().multiply(BigDecimal.valueOf(bomInfo.getQuantity())));
        }
        bomDetailService.save(bomDetailDo);
        return bomDetailId;
    }

    public Set<Long> handleAddBomForBelong(Long belongId, List<BomInfoDTO> bomS, List<BomDetailDo> bomDetailDos) {

        Map<Long, BomDetailDo> existDetailDoMap = bomDetailDos.stream()
                .collect(Collectors.toMap(BomDetailDo::getBomTemplateId, Function.identity()));
        Set<Long> existDetailTemplateIds = existDetailDoMap.keySet();

        Set<Long> newBomIdS = new HashSet<>();

        for (BomInfoDTO bomInfoDTO : bomS) {
            BomVo bom = bomInfoDTO.getBom();
//            // 新建bom,插入detail
//            if (Objects.isNull(bom.getId())) {
//                Long id = this.creatBom(bom);
//                bom.setId(id);
//                newBomIdS.add(initBomDetailId(bomInfoDTO, belongId, id));
//                continue;
//            }
            // 已有bom
            if (existDetailTemplateIds.contains(bom.getId())) {
                // 已存在detail,更新bom和detail
                BomDetailDo bomDetailDo = existDetailDoMap.get(bom.getId());
                newBomIdS.add(bomDetailDo.getId());
                updateBom(bom);
                bomDetailDo.setCount(bomInfoDTO.getQuantity());
                bomDetailDo.setIsDeleted(false);
                if ("fixed".equals(bom.getFeeModel())) {
                    bomDetailDo.setTotalPrice(bom.getFeeAmount());
                } else {
                    bomDetailDo.setTotalPrice(bom.getFeeAmount().multiply(BigDecimal.valueOf(bomInfoDTO.getQuantity())));
                }
                bomDetailDo.setUpdateTime(new Date());
                bomDetailService.updateById(bomDetailDo);
            } else {
                // 新增detail
                newBomIdS.add(initBomDetailId(bomInfoDTO, belongId, bom.getId()));
            }
            // 已有bom且存在detail但不在bomS列表需要删除
            bomDetailDos.forEach(bomDetailDo -> {
                if (!existDetailTemplateIds.contains(bomDetailDo.getBomTemplateId())) {
                    bomDetailDo.setIsDeleted(true);
                    bomDetailDo.setUpdateTime(new Date());
                    bomDetailDo.setDeleteTime(new Date());
                    bomDetailService.updateById(bomDetailDo);
                }
            });
        }
        return newBomIdS;
    }
}
